#ifndef __BOOK_H
#define __BOOK_H

#include <math.h>

#include <string>
#include <vector>
#include <map>
#include <set>

#include "io.h"

struct Book {
    std::string archive;
    std::string filename;
    std::string title;
    std::vector< std::vector<std::string> > author;
    std::string lang;

    enum tag_t {
        PLAIN,
        TITLE,
        POEM,
        LABEL
    };

    std::set<size_t> title_index;
    std::set<size_t> author_index;

    size_t n;
    std::map<size_t,size_t> w;

    std::vector< std::pair<tag_t,std::string> > text;

    Book() : n(0) {}
};


void write_terms(const std::map<size_t,size_t>& w, const std::string& filename) {

    Sink fout(filename, true);

    fout << (size_t)0;

    fout << w.size();

    for (const auto& z : w) {
        fout << z.first;
        fout << z.second;
    }
}

void write_book(const Book& book, const std::string& filename) {

    Sink fout(filename, true);

    fout << book.archive.size();
    fout << book.archive;
    fout << book.filename.size();
    fout << book.filename;
    fout << book.title.size();
    fout << book.title;
    fout << book.author.size();

    for (const auto& z : book.author) {
        fout << z[0].size();
        fout << z[0];
        fout << z[1].size();
        fout << z[1];
        fout << z[2].size();
        fout << z[2];
    }

    fout << book.title_index.size();
    for (size_t z : book.title_index) {
        fout << z;
    }

    fout << book.author_index.size();
    for (size_t z : book.author_index) {
        fout << z;
    }

    fout << book.n;

    fout << book.w.size();

    for (const auto& z : book.w) {
        fout << z.first;
        fout << z.second;
    }

    fout << book.text.size();

    for (const auto& z : book.text) {
        fout << (int)z.first;
        fout << z.second;
    }

}

void write_digest(const Book& book, const std::map<size_t,double>& tfidf, const std::string& filename) {

    Sink fout(filename, true);

    fout << book.title.size();
    fout << book.title;
    fout << book.author.size();

    for (const auto& z : book.author) {
        fout << z[0].size();
        fout << z[0];
        fout << z[1].size();
        fout << z[1];
        fout << z[2].size();
        fout << z[2];
    }

    fout << book.n;

    fout << tfidf.size();

    for (const auto& z : tfidf) {
        fout << z.first;
        fout << z.second;
    }
}

bool read_terms(std::map<size_t, size_t>& out, Source& fin) {

    try {

        size_t tmp;
        
        fin >> tmp;
        
        if (tmp != 0) {
            throw std::runtime_error("Oops, malformed terms.dat");
        }

        fin >> tmp;

        for (size_t i = 0; i < tmp; ++i) {
            size_t term;
            size_t n;
            fin >> term;
            fin >> n;
            out[term] = n;
        }

        return true;

    } catch(...) {
        return false;
    }
}

bool read_book(Book& book, Source& fin) {

    size_t n;
    size_t z;

    try {

        fin >> n;
        read_string(fin, book.archive, n);
        fin >> n;
        read_string(fin, book.filename, n);
        fin >> n;
        read_string(fin, book.title, n);
        fin >> z;
        for (size_t i = 0; i < z; ++i) {
            book.author.push_back(std::vector<std::string>(3));
            fin >> n;
            read_string(fin, book.author.back()[0], n);
            fin >> n;
            read_string(fin, book.author.back()[1], n);
            fin >> n;
            read_string(fin, book.author.back()[2], n);
        }

        fin >> n;
        for (size_t i = 0; i < n; ++i) {
            size_t x;
            fin >> x;
            book.title_index.insert(x);
        }

        fin >> n;
        for (size_t i = 0; i < n; ++i) {
            size_t x;
            fin >> x;
            book.author_index.insert(x);
        }

        fin >> book.n;

        fin >> n;
        for (size_t i = 0; i < n; ++i) {
            size_t x;
            size_t y;
            fin >> x;
            fin >> y;
            book.w[x] = y;
        }

        fin >> n;
        for (size_t i = 0; i < n; ++i) {
            int x;
            std::string y;
            fin >> x;
            fin >> y;
            book.text.push_back(std::make_pair((Book::tag_t)x, y));
        }

        return true;

    } catch(...) {
        return false;
    }
}

bool read_digest(Book& book, std::map<size_t, double>& out, Source& fin) {

    size_t n;
    size_t z;

    try {

        fin >> n;
        read_string(fin, book.title, n);
        fin >> z;
        for (size_t i = 0; i < z; ++i) {
            book.author.push_back(std::vector<std::string>(3));
            fin >> n;
            read_string(fin, book.author.back()[0], n);
            fin >> n;
            read_string(fin, book.author.back()[1], n);
            fin >> n;
            read_string(fin, book.author.back()[2], n);
        }

        fin >> book.n;

        fin >> n;

        for (size_t i = 0; i < n; ++i) {
            size_t term;
            double w;
            fin >> term;
            fin >> w;
            out[term] = w;
        }

        return true;

    } catch(...) {
        return false;
    }
}

#endif
